package com.ufo.id;
/**
 * 雪花算法的简易实现，不处理时间回拨问题
 *
 * 0 - 00000000 00000000 00000000 00000000 00000000 0 - 00000 00000 - 00000000 0000
 * 5个数据段说明
 * part1: 1 bit  : 0 表示正负
 * part2: 41 bit : 时间戳，单位毫秒。 2^41-1个毫秒值，换算成年为69年的时间
 * part3: 5 bit  : 数据中心id
 * part4: 5 bit  : 机器id
 * part5: 2 bit : 自增序号，单实例1毫秒同时生成的id的序号。最大支持 2 ^ 12 - 1 = 4095 个id同时生成
 */
public class SnowFlake {
    public static void main(String[] args){
        SnowFlake.setSnowId(1,2);
        for(int i=0;i<10;i++){
            System.out.println(SnowFlake.nextId());
        }
        SnowFlake.snowId.readIdInParts(204492029300736L);
    }

    static SnowId snowId;

    public static void setSnowId(long dataCenterId,long workerId) {
        snowId=new SnowId(dataCenterId,workerId);
    }

    public static long nextId() {
        return snowId.nextId();
    }

    static class SnowId {
        private final static long epoch=1651680000000L;//初始化起始时间
        private final static int dataCenterIdBits=5;
        private final static int workerIdBits=5;
        private final static int sequencesBits=12;
        private final static long maxDataCenterId= ~(-1L << dataCenterIdBits);
        private final static long maxWorkerId= ~(-1L << workerIdBits);
        private final static long maxSequences= ~(-1L << sequencesBits); //2^12-1
        //左移位数
        private final static int timestampShift=sequencesBits+workerIdBits+dataCenterIdBits;
        private final static int dataCenterIdShift=sequencesBits+workerIdBits;
        private final static int workerIdShift=sequencesBits;

        private long workerId;
        private long dataCenterId;
        private long sequence;
        private long lastTimeStamp=-1L;

        public SnowId(long dataCenterId, long workerId){
            this(workerId,dataCenterId,0);
        }
        SnowId(long dataCenterId, long workerId, long sequence){
            assert workerId>0 && workerId<maxWorkerId;
            assert dataCenterId>0 && dataCenterId<maxDataCenterId;
            this.dataCenterId=dataCenterId;
            this.workerId=workerId;
            this.sequence=sequence;
        }

        private synchronized long nextId(){
            long timestamp=generateTimeMil();
            if(timestamp<lastTimeStamp){
                throw new RuntimeException("时钟回拨，拒绝生成");
            }
            if(timestamp==lastTimeStamp){
                sequence = (sequence+1) & maxSequences;
                if(sequence==0){
                    timestamp=untilNextMills(lastTimeStamp);
                }
            }else{
                sequence=0;
            }
            lastTimeStamp=timestamp;
            System.out.println("t="+(timestamp-epoch)+",c="+dataCenterId+",w="+workerId+",s="+sequence);
            return ( (timestamp-epoch) << timestampShift ) |
                    (dataCenterId << dataCenterIdShift) |
                    (workerId << workerIdShift) |
                    sequence;
        }
        //等到下一毫秒
        private long untilNextMills(long lastTimeStamp) {
            long timestamp=generateTimeMil();
            while(timestamp<=lastTimeStamp){
                timestamp=generateTimeMil();
            }
            return timestamp;
        }

        private long generateTimeMil(){
            return System.currentTimeMillis();
        }

        public String readIdInParts(long snowId){
            System.out.println("snowId="+snowId);
            System.out.println("snowId in bytes="+Long.toBinaryString(snowId));
            long s = snowId & maxSequences;
            long w = (snowId & (maxWorkerId<<workerIdShift))>>workerIdShift;
            long c = (snowId & (maxDataCenterId<<dataCenterIdShift))>>dataCenterIdShift;
            long t = (snowId & (-1L<<timestampShift))>>timestampShift;
            System.out.println("timestamp="+(t+epoch)+",dataCenterId="+c+",workerId="+w+",第"+s+"个序号");
            return null;
        }
    }

}
